﻿using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Security;
using Microsoft.IdentityModel.Claims;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Claims;
using Microsoft.SharePoint.WebControls;
using System.Configuration;
using System.Security.Principal;

using CNRS.CCP.Config;
using CNRS.CCP.Connector;
using CNRS.CCP.DAO;

namespace CNRS.CCP
{
	/// <summary>
	/// Code based on "Claims Walkthrough: Writing Claims Providers for Sharepoint 2010"
	/// Ref : http://msdn.microsoft.com/en-us/library/ie/ff699494.aspx
	/// ref : http://blogs.msdn.com/b/kaevans/archive/2013/05/26/fixing-people-picker-for-saml-claims-users-using-ldap.aspx
	/// </summary>
	public class ClaimProvider : SPClaimProvider
	{
		#region Members
		protected List<string> _ClaimTypeKeyList = new List<string>();

		//protected string _TypeKeyLDAP = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";
		//protected string _TypeKeyExt = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";

		public const string PARAMS_PATH = "C:\\CNRSccp\\";
		public const string PARAMS_FILE_CLAIMSPARAM = "Farm.json";
		
		protected const string PROHIBITED_CHAR_LIST = "&=!()~|><";

		public const string PROVIDER_NAME_INTERNAL = "CNRSccp";
		public const string PROVIDER_NAME_DISPLAY = "CNRSccp";
		
		protected const string PROVIDER_NODE_KEY_LDAP = "emailaddress";
		protected const string PROVIDER_NODE_NAME_LDAP = "LDAP";
		protected const string PROVIDER_LDAP_TYPEKEY = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";

		protected const string PROVIDER_NODE_KEY_EXTERN = "emailaddressext";
		protected const string PROVIDER_NODE_NAME_EXTERN = "Externe";
		protected const string PROVIDER_EXTERN_TYPEKEY = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";


		private bool _ActiveDebug = true;
		private Dictionary<string, bool> _IgnoredWebApps = new Dictionary<string, bool>();
		#endregion

		#region Properties
		/// <summary>
		/// Nom du Claims Provider
		/// </summary>
		public override string Name
		{
			get
			{
				return PROVIDER_NAME_INTERNAL;
			}
		}

		/// <summary>
		/// Type du Claims de retour (string)
		/// </summary>
		public static string CNRSClaimValueType
		{
			get
			{
				return Microsoft.IdentityModel.Claims.ClaimValueTypes.String;
			}
		}

		/// <summary>
		/// Tells that CNRSccp supports EntityInformation
		/// </summary>
		public override bool SupportsEntityInformation
		{
			get
			{
				if(ClaimsParameters.Instance.Debug)
					return true;
				return false;
			}
		}

		/// <summary>
		/// CNRSccp supports Hierarchy
		/// </summary>
		public override bool SupportsHierarchy
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		/// CNRSccp supports Resolve
		/// </summary>
		public override bool SupportsResolve
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		/// CNRSccp supports search
		/// </summary>
		public override bool SupportsSearch
		{
			get
			{
				return true;
			}
		}
		#endregion

		#region Constructors
		public ClaimProvider(string displayName) : base(displayName)
		{
			//Test debug
			ActiveDebug();

			if (!_ClaimTypeKeyList.Contains(ClaimsParameters.Instance.IdClaimType))
				_ClaimTypeKeyList.Add(ClaimsParameters.Instance.IdClaimType);

			if (!_ClaimTypeKeyList.Contains(ClaimsParameters.Instance.IdClaimType))
				_ClaimTypeKeyList.Add(ClaimsParameters.Instance.IdClaimType);
		}
		#endregion

		#region Methods
		#region Methodes de Log
		/// <summary>
		/// Log un message d'erreur
		/// </summary>
		/// <param name="message"></param>
		/// <returns></returns>
		private TimeSpan LogStartMethodDebug(string message)
		{
			TimeSpan t = TimeSpan.FromTicks(DateTime.Now.TimeOfDay.Ticks);
			if (_ActiveDebug)
			{
				LogHelper.Log(LogCategorie.ClaimsProvider, "Entering the method " + message + "...", TraceSeverity.Monitorable); //Verbose n'est pas loggé par défaut, on utilise donc Unexpected.
			}
			return t;
		}

		/// <summary>
		/// Log un message d'erreur
		/// </summary>
		/// <param name="message"></param>
		/// <returns></returns>
		private void LogEndMethodDebug(string message, TimeSpan startT)
		{
			if (_ActiveDebug)
			{
				TimeSpan currentT = TimeSpan.FromTicks(DateTime.Now.TimeOfDay.Ticks);
				LogHelper.Log(LogCategorie.ClaimsProvider, "Leaving the method " + message + "... Method duration " + currentT.Subtract(startT).ToString(), TraceSeverity.Monitorable); //Verbose n'est pas loggé par défaut, on utilise donc Unexpected.
			}
		}

		/// <summary>
		/// Méthode de debug utilisée en cas de plantage, mettre la variable "debug_claims" à 1 dans l'appsettings du web.config
		/// </summary>
		private void LogDebug(string message)
		{
			if (_ActiveDebug)
			{
				LogHelper.Log(LogCategorie.ClaimsProvider, message, TraceSeverity.Monitorable); //Verbose n'est pas loggé par défaut, on utilise donc Unexpected.
			}
		}

		/// <summary>
		/// Méthode de debug utilisée en cas de plantage, mettre la variable "debug_claims" à 1 dans l'appsettings du web.config
		/// </summary>
		private void LogDebug(string message, string[] values)
		{
			if (_ActiveDebug)
			{
				string value = string.Empty;
				foreach (string val in values)
					value += "\"" + val + "\" ";
				LogHelper.Log(LogCategorie.ClaimsProvider, message + " " + value, TraceSeverity.Monitorable); //Verbose n'est pas loggé par défaut, on utilise donc Unexpected.
			}
		}

		/// <summary>
		/// Méthode de debug utilisée en cas de plantage, mettre la variable "debug_claims" à 1 dans l'appsettings du web.config
		/// </summary>
		private void LogDebug(string message, List<string> values)
		{
			if (_ActiveDebug)
			{
				string value = string.Empty;
				foreach (string val in values)
					value += "\"" + val + "\" ";
				LogHelper.Log(LogCategorie.ClaimsProvider, message + " " + value, TraceSeverity.Monitorable); //Verbose n'est pas loggé par défaut, on utilise donc Unexpected.
			}
		}

		/// <summary>
		/// Méthode lancée au début du Claims pour vérifier si on log les informations
		/// </summary>
		private void ActiveDebug()
		{
			try
			{
				if (ClaimsParameters.Instance.Debug)
				{
					_ActiveDebug = true;
					LogHelper.Log(LogCategorie.ClaimsProvider, "CLAIMS Logging Enabled", TraceSeverity.Monitorable); //Verbose n'est pas loggé par défaut, on utilise donc Unexpected.
				}
			}
			catch (Exception e)
			{
				LogHelper.Log(LogCategorie.ClaimsProvider, "Error CLAIMS variable debuggage " + e.Message, TraceSeverity.Unexpected); //Verbose n'est pas loggé par défaut, on utilise donc Unexpected.
			}
		}
		#endregion

		#region Methodes de configuration du Claims
		
		/// <summary>
		/// Claim augmentation can be done here. 
		/// This gives us the opprtinuty to dump all claims value in debug mode
		/// </summary>
		/// <param name="context"></param>
		/// <param name="entity"></param>
		/// <param name="claims"></param>
		protected override void FillClaimsForEntity(Uri context, SPClaim entity, List<SPClaim> claims)
		{
			TimeSpan t = LogStartMethodDebug("FillClaimsForEntity");

			if (entity == null)
				throw new ArgumentNullException("entity");

			if (claims == null)
				throw new ArgumentNullException("claims");

			if(_ActiveDebug)
			{
				//Figure out who the user is so we know what term to add to their claim.
				//string userName = ClaimsHelper.GetDecodedClaim(entity.Value, false);
				LogDebug(string.Format("Entity: '{0}'", entity.Value));

				foreach (SPClaim claim in claims)
				{
					LogDebug(string.Format("Claims Type: {0}, Value: {1}, ValueType: {2}", claim.ClaimType, claim.Value, claim.ValueType));
				}
			}
			
			LogEndMethodDebug("FillClaimsForEntity", t);
		}

		protected override void FillClaimTypes(List<string> claimTypes)
		{
			TimeSpan t = LogStartMethodDebug("FillClaimTypes");

			if (claimTypes == null)
				throw new ArgumentNullException("claimTypes");

			LogDebug("Entity types:", _ClaimTypeKeyList);
			foreach (string typeKey in _ClaimTypeKeyList)
			{
				//Add our claim type.
				claimTypes.Add(typeKey);
			}

			LogEndMethodDebug("FillClaimTypes", t);
		}

		protected override void FillClaimValueTypes(List<string> claimValueTypes)
		{
			TimeSpan t = LogStartMethodDebug("FillClaimValueTypes");

			if (claimValueTypes == null)
				throw new ArgumentNullException("claimValueTypes");

			//Add our claim value type.
			claimValueTypes.Add(CNRSClaimValueType);

			LogEndMethodDebug("FillClaimValueTypes", t);
		}

		/// <summary>
		/// Précise les types d'entités à récupérer
		/// </summary>
		/// <param name="entityTypes"></param>
		protected override void FillEntityTypes(List<string> entityTypes)
		{
			TimeSpan t = LogStartMethodDebug("FillEntityTypes");

			//Return the type of entity claim we are using
			entityTypes.Add(SPClaimEntityTypes.FormsRole);
			entityTypes.Add(SPClaimEntityTypes.User);

			LogEndMethodDebug("FillEntityTypes", t);
		}
		#endregion

		#region People Picker Hierarchy provisionning
		/// <summary>
		/// Méthode de approvisionnement de l'arborescence
		/// </summary>
		/// <param name="context"></param>
		/// <param name="entityTypes"></param>
		/// <param name="hierarchyNodeID"></param>
		/// <param name="numberOfLevels"></param>
		/// <param name="hierarchy"></param>
		protected override void FillHierarchy(Uri context, string[] entityTypes, string hierarchyNodeID, int numberOfLevels, SPProviderHierarchyTree hierarchy)
		{
			TimeSpan t = LogStartMethodDebug("FillHierarchy");
			LogDebug("context: " + context);
			LogDebug("entityTypes:", entityTypes);
			LogDebug("hierarchyNodeID: " + hierarchyNodeID);
			LogDebug("numberOfLevels: " + numberOfLevels);
			LogDebug("hierarchy: " + hierarchy.Name);

			//On ne récupère que les Users ou les Roles
			if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole) && !EntityTypesContain(entityTypes, SPClaimEntityTypes.User))
				return;

			//Check to see if the hierarchyNodeID is null; it will be when the control 
			//is first loaded but if a user clicks on one of the nodes it will return
			//the key of the node that was clicked.
			switch (hierarchyNodeID)
			{
				//Pas de click
				case null:
					FillHierarchyAction(hierarchy);
					break;
				default:
					break;
			}

			LogEndMethodDebug("FillHierarchy", t);
		}

		/// <summary>
		/// Méthode de approvisionnement du contenu de l'arborescence (les différents claims)
		/// </summary>
		/// <param name="site"></param>
		/// <param name="hierarchy"></param>
		private void FillHierarchyAction(SPProviderHierarchyTree hierarchy)
		{
			TimeSpan t = LogStartMethodDebug("FillHierarchyAction");

			//Chargement branche LDAP
			hierarchy.AddChild(new
				SPProviderHierarchyNode(
						PROVIDER_NAME_INTERNAL,
						PROVIDER_NODE_NAME_LDAP,
						PROVIDER_NODE_KEY_LDAP,
						true));
			if (!_ClaimTypeKeyList.Contains(ClaimsParameters.Instance.IdClaimType))
				_ClaimTypeKeyList.Add(ClaimsParameters.Instance.IdClaimType);

			//Chargement branche Externe
			hierarchy.AddChild(new
				SPProviderHierarchyNode(
						PROVIDER_NAME_INTERNAL,
						PROVIDER_NODE_NAME_EXTERN,
						PROVIDER_NODE_KEY_EXTERN,
						true));
			if (!_ClaimTypeKeyList.Contains(ClaimsParameters.Instance.IdClaimType))
				_ClaimTypeKeyList.Add(ClaimsParameters.Instance.IdClaimType);

			//Chargement des Claims
			if (SPContext.Current != null && !TestIgnoredWebApps(SPContext.Current.Web.Url))
			{
				List<ClaimElement> claimsElements = ClaimsParameters.Instance.ClaimElements;
				if (claimsElements != null && claimsElements.Count > 0)
				{
					foreach (ClaimElement claimElement in claimsElements)
					{
						if (!claimElement.Hidden)
						{
							//Fill claim typeKey list
							string typeKey = claimElement.ClaimType;
							if (!_ClaimTypeKeyList.Contains(typeKey))
								_ClaimTypeKeyList.Add(typeKey);

							hierarchy.AddChild(new
								SPProviderHierarchyNode(
									 PROVIDER_NAME_INTERNAL,
									 claimElement.Title,
									 claimElement.ClaimType.Split('/').Last(),
									 true));
						}
					}
				}
			}

			LogEndMethodDebug("FillHierarchyAction", t);
		}
		#endregion

		#region Search claims value
		/// <summary>
		/// Recherche d'abord dans les utilisateurs LDAP, puis Externe, puis les DB SQL
		/// </summary>
		/// <param name="context"></param>
		/// <param name="entityTypes"></param>
		/// <param name="searchPattern"></param>
		/// <param name="hierarchyNodeID"></param>
		/// <param name="maxCount"></param>
		/// <param name="searchTree"></param>
		protected override void FillSearch(Uri context, string[] entityTypes, string searchPattern, string hierarchyNodeID, int maxCount, SPProviderHierarchyTree searchTree)
		{
			TimeSpan t = LogStartMethodDebug(string.Format("FillSearch pattern={0}, maxCount={1}", searchPattern, maxCount));
			LogDebug("Arg 'context' ->", entityTypes);

			//La recherche s'effectue sans le code Claims, on le retire s'il existe.
			searchPattern = ClaimsHelper.GetDecodedClaim(searchPattern, true);

			//On ne récupère que les Users ou les Roles
			if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole) && !EntityTypesContain(entityTypes, SPClaimEntityTypes.User))
				return;

			//Arborescence de recherche
			SPProviderHierarchyNode matchNode = null;

			#region LDAP
			LogDebug("Searching User");

			string ldapPattern = searchPattern.ToLower();
			if (ldapPattern.Count(elt => elt == '*') <= 1)
			{
				if (ldapPattern.Substring(0,1) != "*")
					ldapPattern = "*" + searchPattern.ToLower();
				if (ldapPattern.Substring(ldapPattern.Length-1, 1) != "*")
					ldapPattern = ldapPattern + "*";
			}

			List<DataElement> results = LdapConnector.GetDataElements(ldapPattern);
			if (results != null && results.Count > 0)
			{
				//Add the term node where it should be displayed too.
				if (!searchTree.HasChild(PROVIDER_NODE_KEY_LDAP))
				{
					//Create the node so we can show our match in there too.
					matchNode = new
						SPProviderHierarchyNode(PROVIDER_NAME_INTERNAL,
						PROVIDER_NODE_NAME_LDAP,
						PROVIDER_NODE_KEY_LDAP,
						true);

					//Add it to the tree.
					searchTree.AddChild(matchNode);
				}
				else
				{
					//Get the node for this term.
					matchNode = searchTree.Children.Where(theNode => theNode.HierarchyNodeID == PROVIDER_NODE_KEY_LDAP).First();
				}
				
				//Same sort of code as in search to validate we have a match.
				foreach (DataElement result in results)
				{
					//We have a match, create a matching entity.
					//PickerEntity pe = GetPickerEntity(PROVIDER_LDAP_TYPEKEY, result.Value );
					PickerEntity pe = GetPickerEntity(ClaimsParameters.Instance.IdClaimType, result.Value, result.Display, result.Email);


					//Add the match to our node.
					matchNode.AddEntity(pe);
				}
				LogEndMethodDebug("FillSearch", t);
				return;
			}
			#endregion
			#region External User
			LogDebug("Searching External User");
			if (MappingHelper.CheckEmail(searchPattern.ToLower()))
			{
				//We have a match, create a matching entity.
				string lowerSearch = searchPattern.ToLower();
				PickerEntity pe = GetPickerEntity(ClaimsParameters.Instance.IdClaimType, lowerSearch, lowerSearch, lowerSearch);

				//Add the term node where it should be displayed too.
				if (!searchTree.HasChild(PROVIDER_NODE_KEY_EXTERN))
				{
					//Create the node so we can show our match in there too.
					matchNode = new
						SPProviderHierarchyNode(PROVIDER_NAME_INTERNAL,
						PROVIDER_NODE_NAME_EXTERN,
						PROVIDER_NODE_KEY_EXTERN,
						true);

					//Add it to the tree.
					searchTree.AddChild(matchNode);
				}
				else
					//Get the node for this term.
					matchNode = searchTree.Children.Where(theNode => theNode.HierarchyNodeID == PROVIDER_NODE_KEY_EXTERN).First();

				//Add the match to our node.
				matchNode.AddEntity(pe);
				LogEndMethodDebug("FillSearch", t);
				return;
			}
			#endregion
			#region Searching Data
			LogDebug("Searching Role");
			//SPContext.Current.Web.Url.Contains(Constantes.WEBAPPLICATION_CENTRALE_ADMIN_REF_URL) - permits to disable this part of code for the central admin
			if (SPContext.Current != null
				&& !TestIgnoredWebApps(SPContext.Current.Web.Url))
			{
				FillSearchWorker(searchPattern, searchTree, hierarchyNodeID, matchNode);
			}
			LogEndMethodDebug("FillSearch", t);
			#endregion

		}

		/// <summary>
		/// If no hierarchyNode has been selected, we have to search for all non hidden claims elements
		/// It should be the case in 2013 as the new people picker do not have any hierarchy panel. 
		/// </summary>
		/// <param name="searchPattern"></param>
		/// <param name="searchTree"></param>
		/// <param name="hierarchyNodeID"></param>
		/// <param name="matchNode"></param>
		private void FillSearchWorker(string searchPattern, SPProviderHierarchyTree searchTree, string hierarchyNodeID, SPProviderHierarchyNode matchNode)
		{
			TimeSpan t = LogStartMethodDebug("FillSearchWorker");

			List<ClaimElement> claimsElements = ClaimsParameters.Instance.ClaimElements;
			LogDebug(string.Format("Claims elements '{0}'", claimsElements.Count));
			if (claimsElements != null && claimsElements.Count > 0)
			{
				//Si node préselectionnée
				if (!string.IsNullOrEmpty( hierarchyNodeID))
				{
					foreach (ClaimElement claimElement in claimsElements)
					{
						if (!claimElement.Hidden)
						{
							//If the user have preselected a node
							string key = claimElement.ClaimType.Split('/').Last();
							if (!string.IsNullOrEmpty(key)
								&& key == hierarchyNodeID)
							{
								FillSearchRole(searchPattern, matchNode, searchTree, claimElement);
								break;
							}
						}
					}
				}
				else
				{
					foreach (ClaimElement claimElement in claimsElements)
					{
						if (claimElement != null || !claimElement.Hidden)
						{
							FillSearchRole(searchPattern, matchNode, searchTree, claimElement);
						}
					}
				}
			}

			LogEndMethodDebug("FillSearchWorker", t);
		}

		/// <summary>
		/// Search in a particular node RefList to found a match
		/// </summary>
		/// <param name="currentWeb"></param>
		/// <param name="searchPattern"></param>
		/// <param name="matchNode"></param>
		/// <param name="searchTree"></param>
		/// <param name="node"></param>
		private void FillSearchRole(string searchPattern, SPProviderHierarchyNode matchNode, SPProviderHierarchyTree searchTree, ClaimElement element)
		{
			TimeSpan t = LogStartMethodDebug(string.Format("FillSearchRole for element {0}", element.ClaimType));

			// based on the element providername we search for the provider and call the appropriate helper
			List<DataElement> dataElements = null;
			switch (element.ProviderName)
			{ 
				case "sql" :
					searchPattern = searchPattern + "%";
					dataElements = SQLConnector.GetDataElements(searchPattern, element);
					break;
				case "grouper" :
					dataElements = VootConnector.GetDataElements(searchPattern, element);
					break;
			}

			if (dataElements != null && dataElements.Count > 0)
			{
				// add the claim type to serach result tree
				if (!searchTree.HasChild(element.ClaimType.Split('/').Last()))
				{
					//Create the node so we can show our match in there too.
					matchNode = new
						SPProviderHierarchyNode(PROVIDER_NAME_INTERNAL,
						element.Title,
						element.ClaimType.Split('/').Last(),
						true);

					//Add it to the tree.
					searchTree.AddChild(matchNode);
				}
				else
				{
					//Get the node for this term.
					matchNode = searchTree.Children.Where(theNode => theNode.HierarchyNodeID == element.ClaimType.Split('/').Last()).First();
				}

				foreach (DataElement dataElement in dataElements)
				{
					// Create a picker objet
					PickerEntity pe = GetPickerEntity((element.ClaimType), string.Format(element.Value, dataElement.Value), string.Format(element.Display, dataElement.Value), "");

					// and add it to the node
					matchNode.AddEntity(pe);
				}
			}

			LogEndMethodDebug(string.Format("FillSearchRole for claim {0}", element.ClaimType), t);
		}

		/// <summary>
		/// FillSchema
		/// </summary>
		/// <param name="schema"></param>
		protected override void FillSchema(SPProviderSchema schema)
		{
			//Add the schema element we need at a minimum in our picker node.
			schema.AddSchemaElement(new SPSchemaElement(PeopleEditorEntityDataKeys.DisplayName, "Display Name", SPSchemaElementType.Both));
		}
		#endregion

		#region Resolve claim value
		/// <summary>
		/// Méthode de résolution de l'input du Provider
		/// </summary>
		/// <param name="context"></param>
		/// <param name="entityTypes"></param>
		/// <param name="resolveInput"></param>
		/// <param name="resolved"></param>
		protected override void FillResolve(Uri context, string[] entityTypes, SPClaim resolveInput, List<PickerEntity> resolved)
		{
			LogDebug("->>FillResolve-Claim: entityTypes: " + entityTypes.ToString() + ", Input: " + resolveInput + ", Uri: " + context.ToString());

			//On ne récupère que les Users ou les Roles
			if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole) && !EntityTypesContain(entityTypes, SPClaimEntityTypes.User))
				return;

			FillResolveWorker(context, resolveInput.Value.ToLower(), resolveInput.ClaimType, resolved);
		}

		/// <summary>
		/// Méthode de résolution de l'input du Provider
		/// </summary>
		/// <param name="context"></param>
		/// <param name="entityTypes"></param>
		/// <param name="resolveInput"></param>
		/// <param name="resolved"></param>
		protected override void FillResolve(Uri context, string[] entityTypes, string resolveInput, List<PickerEntity> resolved)
		{
			LogDebug("->>FillResolve-String: entityTypes: " + entityTypes.ToString() + ", Input: " + resolveInput + ", Uri: " + context.ToString());

			//On ne récupère que les Users ou les Roles
			if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole) && !EntityTypesContain(entityTypes, SPClaimEntityTypes.User))
				return;

			FillResolveWorker(context, resolveInput, string.Empty, resolved);
		}


		/// <summary>
		/// Do the Fill resolve job
		/// </summary>
		/// <param name="context"></param>
		/// <param name="resolveInput"></param>
		/// <param name="resolved"></param>
		private void FillResolveWorker(Uri context, string resolveInput, string claimType, List<PickerEntity> resolved)
		{
			TimeSpan t = LogStartMethodDebug("FillResolveWorker");
			LogDebug(string.Format("->>FillResolveWorker: context: {0}, resolveInput", context, resolveInput));

			try
			{
				#region Test LDAP
				LogDebug("Trying LDAP connection...");
				List<DataElement> results = LdapConnector.GetDataElements(resolveInput.ToLower());
				if (results != null && results.Count > 0)
				{
					//Same sort of code as in search to validate we have a match.
					foreach (DataElement result in results)
					{
						//We have a match, create a matching entity.
						PickerEntity pe = GetPickerEntity(ClaimsParameters.Instance.IdClaimType, result.Value, result.Display, result.Email);

						//Add it to the return list of picker entries.
						resolved.Add(pe);
					}
				}
				#endregion
				#region Test Externe User
				else
				{
					LogDebug("No results found, trying Extern user...");
					if (MappingHelper.CheckEmail(resolveInput.ToLower()))
					{ 
						//We have a match, create a matching entity.
						string resolveLower = resolveInput.ToLower();
						PickerEntity pe = GetPickerEntity(ClaimsParameters.Instance.IdClaimType, resolveLower);

						//Add it to the return list of picker entries.
						resolved.Add(pe);
					}
				}
				#endregion
				#region Test SharePoint List
				if (SPContext.Current != null
					&& !TestIgnoredWebApps(SPContext.Current.Web.Url))
				{
					FillResolveRole(resolveInput, claimType, resolved);
				}
				#endregion
			}
			catch (Exception ex)
			{
				LogHelper.Log(LogCategorie.ClaimsProvider, "ResolveInput : " + resolveInput, TraceSeverity.Unexpected);
				LogHelper.Log(LogCategorie.ClaimsProvider, ex.Message, TraceSeverity.Unexpected);
			}

			LogEndMethodDebug("SearchAndResolveEntity", t);
		}


		/// <summary>
		/// Travail de recherche sur listes Unités
		/// </summary>
		/// <param name="site"></param>
		/// <param name="resolveInput"></param>
		/// <param name="resolved"></param>
		private void FillResolveRole(string resolveInput, string claimType, List<PickerEntity> resolved)
		{
			TimeSpan t = LogStartMethodDebug("SearchAndResolveEntityDatabase");
			LogDebug(string.Format("Arg 'resolveInput' -> '{0}'", resolveInput));

			List<ClaimElement> claimElements = ClaimsParameters.Instance.ClaimElements;
			if (claimElements != null && claimElements.Count > 0)
			{
				foreach (ClaimElement claimElement in claimElements)
				{
					
					
					//Fill claims types list
					//string typeKey = claimElement.ClaimType + claimElement.Key;
					if (string.IsNullOrEmpty(claimType) || claimElement.ClaimType.ToLower() == claimType.ToLower())
					{
						List<DataElement> dataElements = null;
						switch (claimElement.ProviderName)
						{
							case "sql":
								dataElements = SQLConnector.GetDataElements(resolveInput, claimElement);
								break;
							case "grouper":
								dataElements = VootConnector.GetDataElements(resolveInput, claimElement);
								break;

						}
						
						LogDebug(string.Format("Searching in claimsElement '{0}'...", claimElement.ClaimType));
						if (!_ClaimTypeKeyList.Contains(claimElement.ClaimType))
							_ClaimTypeKeyList.Add(claimElement.ClaimType);

						//BaseDAO daoList = new BaseDAO(ClaimsParameters.Instance.ConnectionString, claimsElement.DB);
						//if (!string.IsNullOrEmpty(resolveInput))
						//{
						//	DAOParameter parameter = new DAOParameter() { Column = claimsElement.ReferenceID, ParameterName = "@" + claimsElement.ReferenceID, Value = resolveInput };
						//	dataElements = daoList.GetItems(parameter);
						//	LogDebug(string.Format("Elements returned -> '{0}'", dataElements.Count));
						//}

						if (dataElements != null && dataElements.Count > 0)
						{
							//Look to see if the value that is typed in matches any of our terms.
							foreach (DataElement dataElement in dataElements)
							{
								PickerEntity pe = GetPickerEntity((claimElement.ClaimType), string.Format(claimElement.Value, dataElement.Value), string.Format(claimElement.Display, dataElement.Value), "");

								//Add it to the return list of picker entries.
								resolved.Add(pe);
							}
						}
					}
				}
			}

			LogEndMethodDebug("SearchAndResolveEntityDatabase", t);
		}
		#endregion

		#region Methodes outils
		/// <summary>
		/// Vérifie les urls ignorées et renvoie true si le paramètre matches
		/// </summary>
		/// <param name="currentUrl"></param>
		/// <returns></returns>
		private bool TestIgnoredWebApps(string currentUrl)
		{
			if (!_IgnoredWebApps.ContainsKey(currentUrl))
			{
				List<string> ignoredWebApps = ClaimsParameters.Instance.IgnoredWebApps;

				foreach (string ignoredWebApp in ignoredWebApps)
				{
					if (ignoredWebApp.Contains(currentUrl))
					{
						_IgnoredWebApps.Add(currentUrl, true);
						return true;
					}
				}

				_IgnoredWebApps.Add(currentUrl, false);
				return false;
			}

			return _IgnoredWebApps[currentUrl];
		}

		/// <summary>
		/// Créé un PickerEntity en fonction de son claims
		/// </summary>
		/// <param name="ClaimTypeKey"></param>
		/// <param name="ClaimValue"></param>
		/// <returns></returns>
		internal PickerEntity GetPickerEntity(string ClaimTypeKey, string ClaimValue)
		{
			return GetPickerEntity(ClaimTypeKey, ClaimValue, ClaimValue, ClaimValue);
		}

		/// <summary>
		/// Build the people picker entity
		/// </summary>
		/// <param name="ClaimTypeKey"></param>
		/// <param name="ClaimValue"></param>
		/// <returns></returns>
		internal PickerEntity GetPickerEntity(string ClaimTypeKey, string ClaimValue, string DisplayName, string Email)
		{
			TimeSpan t = LogStartMethodDebug(string.Format("GetPickerEntity for {0} with vale {1}", ClaimTypeKey, ClaimValue));

			PickerEntity pe = CreatePickerEntity();
			
			//Set the claim associated with this match.
			pe.Claim = new SPClaim(ClaimTypeKey, ClaimValue, CNRSClaimValueType, SPOriginalIssuers.Format(SPOriginalIssuerType.TrustedProvider, ClaimsParameters.Instance.ADFSReference));

			//Set the tooltip that is displayed when you hover over the resolved claim.
			pe.Description = ClaimProvider.PROVIDER_NAME_DISPLAY + ": " + ClaimValue + " (" + ClaimTypeKey + ")";

			//Set the display test
			pe.DisplayText = DisplayName;
			

			//Add minmal values in the hash table
			pe.EntityData[PeopleEditorEntityDataKeys.DisplayName] = DisplayName;
			pe.EntityData[PeopleEditorEntityDataKeys.Email] = Email;

			//User entity.
			if (ClaimTypeKey == ClaimsParameters.Instance.IdClaimType || ClaimTypeKey == ClaimsParameters.Instance.IdClaimType)
				pe.EntityType = SPClaimEntityTypes.User;
			else
				pe.EntityType = SPClaimEntityTypes.FormsRole;

			//Flag the entry as being resolved.
			pe.IsResolved = true;

			//This is the first part of the description that shows
			//up above the matches, like Role: Forms Authentication when
			//you do an FBA search and find a matching role.
			pe.EntityGroupName = "CNRS term";

			LogEndMethodDebug("GetPickerEntity", t);

			return pe;
		}

		#endregion
		#endregion
	}
}
